﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Data.Objects;
using System.Data.Objects.DataClasses;
using System.Data.SqlClient;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Design;
using System.Linq;
using System.Reflection;
using System.Windows.Forms;
using UtfprLibrary.ComponentModel;
using UtfprLibrary.EntityFramework.ComponentModel;
using UtfprLibrary.EntityFramework.Extensions;
using UtfprLibrary.Windows.Forms.ComponentModel;
using UtfprLibrary.Windows.Forms.Exceptions;
using UtfprLibrary.Windows.Forms.Extensions;
using UtfprLibrary.Windows.Forms.Helpers;

namespace UtfprLibrary.Windows.Forms {
    /// <summary>
    /// TODO
    /// </summary>
    [ProvideToolboxControl("UTFPR", false)]
    [ToolboxBitmap(typeof(ResourceFinder), "Resources.VSD_UtfprBindingNavigator.png")]
    [DefaultProperty("DataSource"), ComplexBindingProperties("DataSource", "DataMember")]
    [Designer("UtfprLibrary.Windows.Forms.ComponentModel.UtfprBindingNavigatorDesigner, UtfprLibrary.WindowsForms.Design")]
    [DefaultBindingProperty("DataSource")]
    [Serializable]
    [Description("")]
    public partial class UtfprBindingNavigator : UtfprBindingNavigatorBase {
        #region Base

        #region ISupportInitialize

        public override void BeginInit() { }

        public override void EndInit() {
            try {
                UtfprBindingNavigatorBaseHelper.bindingsEnabled(this, false);
                UtfprBindingNavigatorBaseHelper.boundDataGridsEnabled(this, false);
                UtfprBindingNavigatorBaseHelper.boundLookupsEnabled(this, false);
                UtfprBindingNavigatorBaseHelper.innerDetailsEnabled(this, false);
            } catch { }
        }

        #endregion

        #region Actions
        
        /// <summary>
        /// TODO
        /// </summary>
        public override void AddNew(CancelEventArgs ce) {
            State.Add();
            updateStatusStrip();

            var bindingSource = UtfprBindingNavigatorBaseHelper2.GetMasterBindingSource(this);
            bindingSource.SuspendBinding();
            
            int currentPosition = -1;
            var progress = new UtfprFormProgress(new DoWorkEventHandler(
                delegate {
                    this.Invoke(new MethodInvoker(delegate {
                        UtfprBindingNavigatorBaseHelper2.GetJustBindableControlsFrom(this)
                            .ForEach(a => a.Enabled = true);
                        //CurrencyManager.Refresh();
                        currentPosition = CurrencyManager.AddNewUpdatingPosition();                        
                    }));                    
                }
            ));

            if (progress.ShowDialog() == DialogResult.OK) {
                //FindForm().Refresh();
                bindingSource.ResumeBinding();
                base.Position = currentPosition;

                if (CurrentMode == UBNModeEnum.Details) {
                    UtfprBindingNavigatorBaseHelper.boundDataGridsEnabled(this, AllowEditDataGridViews);
                    UtfprBindingNavigatorBaseHelper.boundDataGridsReadOnly(this, !AllowEditDataGridViews);
                }
            }           

            updateNavigationUI();
        }

        //public override void AddNew(CancelEventArgs ce) {
        //    if (this.CurrencyManager != null && this.CurrencyManager.List is IBindingList && !ce.Cancel) {
        //        State.Add();
        //        updateStatusStrip();

        //        var bs = (BindingSource)UtfprBindingNavigatorBaseHelper.findRootSourceNavigator(this).DataSource;
        //        bs.SuspendBinding();

        //        var progress = new UtfprFormProgress(new DoWorkEventHandler(
        //            delegate {
        //                this.Invoke(new MethodInvoker(delegate {                        
        //                    UtfprBindingNavigatorBaseHelper.bindingsEnabled(this, true);
        //                    UtfprBindingNavigatorBaseHelper.boundLookupsEnabled(this, true);

        //                    if (CurrentMode == UBNModeEnum.Master) {
        //                        UtfprBindingNavigatorBaseHelper.rootSourceDataGridsEnabled(this, true);
        //                    } else if (CurrentMode == UBNModeEnum.Details) {
        //                        UtfprBindingNavigatorBaseHelper.boundDataGridsEnabled(this, false);
        //                    }
        //                }));

        //                if (CurrentMode == UBNModeEnum.Master) {
        //                    UtfprBindingNavigatorBaseHelper.clearRootSourceBindingSources(this);
        //                    UtfprBindingNavigatorBaseHelper.clearRootFormLookups(this);
        //                } else if (CurrentMode == UBNModeEnum.Details) {
        //                    UtfprBindingNavigatorBaseHelper.clearInnerLookups(this);
        //                }

        //                var bl = this.CurrencyManager.List as IBindingList;
        //                var newItem = bl.AddNew();
        //                this.CurrencyManager.Position = bl.IndexOf(newItem);
        //            }
        //        ));
        //        if (progress.ShowDialog() == DialogResult.OK) {
        //            FindForm().Refresh();
        //            bs.ResumeBinding();
        //        }
        //    }

        //    UpdateNavigationUI();
        //}

        /// <summary>
        /// TODO
        /// </summary>
        public override void Edit(CancelEventArgs ce) {
            State.Edit();
            updateStatusStrip();

            UtfprBindingNavigatorBaseHelper.bindingsEnabled(this, true);
            UtfprBindingNavigatorBaseHelper.firstLevelInnerDetailsEnabled(this, true);
            UtfprBindingNavigatorBaseHelper.boundLookupsEnabled(this, true);

            if (CurrentMode == UBNModeEnum.Details) {
                UtfprBindingNavigatorBaseHelper.boundDataGridsEnabled(this, AllowEditDataGridViews);
                UtfprBindingNavigatorBaseHelper.boundDataGridsReadOnly(this, !AllowEditDataGridViews);
            }

            updateNavigationUI();
        }

        /// <summary>
        /// TODO
        /// </summary>
        public override void Save(CancelEventArgs ce) {
            UtfprCrud.UtfprByKey.Select();
            CurrencyManager.EndCurrentEdit();

            if (this.CurrentMode == UBNModeEnum.Master) {
                foreach (var nav in UBNDetails) {
                    ((UtfprBindingNavigator)nav).CurrencyManager.EndCurrentEdit();
                }

                var bs = (BindingSource)UtfprBindingNavigatorBaseHelper.findRootSourceNavigator(this).DataSource;
                var pos = bs.Position;

                //bs.SuspendBinding();
                var progress = new UtfprFormProgress(new DoWorkEventHandler(
                    delegate {
                        if (EntityDataSource != null) {
                            ((IEntityDataSource)EntityDataSource).Add(((BindingSource)UtfprBindingNavigatorBaseHelper.findRootSourceNavigator(this).DataSource).List, EntityState.Detached);
                            ((IEntityDataSource)EntityDataSource).Add(UtfprBindingNavigatorBaseHelper.findRootSourceNavigator(this).List, EntityState.Detached);
                            ((IEntityDataSource)EntityDataSource).SaveChanges();
                        }
                    }
                ));

                progress.ShowDialog();
                if (progress.error != null) {
                    throw progress.error;
                } else {
                    progress.Dispose();
                }
                //bs.ResumeBinding();
                bs.Position = pos;
                _rootEntity = (EntityObject)CurrencyManager.Current;
            }

            State.Save();

            UtfprBindingNavigatorBaseHelper.bindingsEnabled(this, false);
            UtfprBindingNavigatorBaseHelper.innerDetailsEnabled(this, false);
            UtfprBindingNavigatorBaseHelper.allDataGridsEnabled(this, true);
            UtfprBindingNavigatorBaseHelper.innerLookupsEnabled(this, false);

            foreach (var navigator in UtfprBindingNavigatorBaseHelper.getInnerSources(this)) {
                UtfprBindingNavigatorBaseHelper.bindingsEnabled(navigator, false);
                UtfprBindingNavigatorBaseHelper.innerDetailsEnabled(navigator, false);
                UtfprBindingNavigatorBaseHelper.innerLookupsEnabled(navigator, false);
                UtfprBindingNavigatorBaseHelper.allDataGridsEnabled(navigator, true);
            }

            updateStatusStrip();
            updateNavigationUI();
        }

        /// <summary>
        /// TODO
        /// </summary>
        public override void Delete(CancelEventArgs ce) {
            if (CurrencyManager != null && CurrencyManager.List.Count > 0 && CurrencyManager.Current != null && CurrencyManager.List is IBindingList && !ce.Cancel) {
                if (CurrentMode == UBNModeEnum.Master) {
                    State.Delete();
                    UtfprBindingNavigatorBaseHelper.bindingsEnabled(this, false);

                    foreach (var n in UBNDetails) {
                        ((UtfprBindingNavigator)n).CurrencyManager.EndCurrentEdit();
                    }

                    //Validate();
                    CurrencyManager.EndCurrentEdit();

                    var _currentItem = new CurrentItemEventArgs((EntityObject)CurrencyManager.Current);
                    var currentItemType = CurrencyManager.Current.GetType();

                    OnRemovingItem(_currentItem);

                    ((IEntityDataSourceProvider)EntityDataSource).EntityDataSource.ObjectContext
                        .ObjectStateManager.ChangeObjectState(_currentItem.Item, EntityState.Deleted);

                    foreach (var entSet in EntityDataSource.EntitySets.Where(es => es.ElementType == currentItemType)) {
                        entSet.List.Remove(_currentItem.Item);
                    }

                    EntityDataSource.SaveChanges();

                    UtfprQuery.BindingSource.List.Remove(_currentItem.Item);

                    OnRemovedItem(_currentItem);
                } else if (this.CurrentMode == UBNModeEnum.Details) {
                    State.Delete();
                    UtfprBindingNavigatorBaseHelper.bindingsEnabled(this, false);
                    //Validate();

                    var currentItemType = CurrencyManager.Current.GetType();
                    foreach (var entSet in EntityDataSource.EntitySets.Where(es => es.ElementType == currentItemType)) {
                        entSet.Query.GetType().GetMethod("DeleteObject").Invoke(entSet.Query, new object[] { CurrencyManager.Current });
                        CurrencyManager.List.Remove(CurrencyManager.Current);
                    }
                }
                updateStatusStrip();
            }
            updateNavigationUI();
        }

        /// <summary>
        /// TODO
        /// </summary>
        public override void Cancel(CancelEventArgs ce) {
            State.Cancel();

            UtfprBindingNavigatorBaseHelper.innerDetailsEnabled(this, false);
            this.CurrencyManager.CancelCurrentEdit();

            if (this.CurrentMode == UBNModeEnum.Master) {
                var bs = (BindingSource)UtfprBindingNavigatorBaseHelper.findRootSourceNavigator(this).DataSource;
                bs.RaiseListChangedEvents = false;
                bs.SuspendBinding();

                var progress = new UtfprFormProgress(new DoWorkEventHandler(
                   delegate {
                       if (EntityDataSource != null) {
                           EntityObject currentObject = null;
                           try {
                               if (this.CurrencyManager.Current != null) {
                                   currentObject = (EntityObject)this.CurrencyManager.Current;
                               }
                           } catch {
                               foreach (var entry in EntityDataSource.ObjectContext.ObjectStateManager.GetObjectStateEntries(EntityState.Added)) {
                                   if (entry.Entity != null) {
                                       EntityDataSource.ObjectContext.Detach(entry.Entity);
                                   }
                               }
                           }

                           if (currentObject != null) {
                               if (currentObject.EntityState == EntityState.Deleted) {
                                   EntityDataSource.ObjectContext.ObjectStateManager
                                       .ChangeObjectState(currentObject, EntityState.Unchanged);
                               }

                               refreshModifiedEntities(RefreshMode.StoreWins);
                               detachRelatedEntities(currentObject);
                           }

                           ((IEntityDataSource)EntityDataSource).CancelChanges();

                           if (_rootEntity != null && _rootEntity.EntityKey != null) {
                               if (KeyMember == null || KeyMember == string.Empty) {
                                   throw new ArgumentNullException("KeyMember cannot be null.");
                               }
                               _lastId = _rootEntity.EntityKey.EntityKeyValues.Single(a => a.Key == KeyMember).Value;
                               queryByLastId();
                           }
                       }
                   }
                ));                
                
                if (progress.ShowDialog() == DialogResult.OK) {
                    FindForm().Refresh();
                    bs.RaiseListChangedEvents = true;
                    bs.ResumeBinding();
                }

                UtfprBindingNavigatorBaseHelper.bindingsEnabled(this, false);
                foreach (var detail in UBNDetails) {
                    UtfprBindingNavigatorBaseHelper.bindingsEnabled(detail, false);
                }
                UtfprBindingNavigatorBaseHelper.innerLookupsEnabled(this, false);
            } else if (CurrentMode == UBNModeEnum.Details) {
                if (CurrentState == UBNStateEnum.Navigating) {
                    EntityDataSource.ObjectContext.Detach(this.CurrencyManager.Current);
                    this.CurrencyManager.RemoveAt(this.CurrencyManager.Position);
                }

                UtfprBindingNavigatorBaseHelper.boundDataGridsEnabled(this, true);
                UtfprBindingNavigatorBaseHelper.innerLookupsEnabled(this, false);
                UtfprBindingNavigatorBaseHelper.bindingsEnabled(this, false);
            }

            UtfprBindingNavigatorBaseHelper.allDataGridsEnabled(this, true);

            updateStatusStrip();
            updateNavigationUI();
        }

        /// <summary>
        /// TODO
        /// </summary>
        public override void Refresh(CancelEventArgs ce) {
            try {
                //if (EntityDataSource != null) {
                //    ((IEntityDataSource)EntityDataSource).Refresh();
                //}

                MessageBox.Show("not implemented");

                updateStatusStrip();
            } catch (Exception ex) {
                throw new UtfprWFFormsException("", ex);
            }

            updateNavigationUI();
        }

        /// <summary>
        /// TODO
        /// </summary>
        public override void Duplicate(CancelEventArgs ce) {
            try {
                //var entityToCopy = BindingSource.Current as EntityObject;
                //Cancel();
                //AddNew();
                //var newEntity = BindingSource.Current as EntityObject;
                //entityToCopy.DuplicateEntity(newEntity, null);
            } catch (Exception ex) {
                throw new UtfprWFControlsException("", ex);
            }

            updateNavigationUI();
        }

        /// <summary>
        /// TODO
        /// </summary>
        public override void Query(CancelEventArgs ce) {
            try {
                EntityObject entity = null;

                if (UtfprQuery != null) {
                    entity = (EntityObject)UtfprBindingNavigatorBaseHelper.getCurrentEntity(UtfprQuery);

                    var rootSource = UtfprBindingNavigatorBaseHelper.findRootSourceNavigator(this);

                    if (entity != null) {
                        State.Search();

                        var alreadyAddedEntity = rootSource.CurrencyManager.List.Cast<EntityObject>()
                            .SingleOrDefault(e => e.EntityKey == entity.EntityKey);

                        if (alreadyAddedEntity != null) {
                            if (rootSource.CurrencyManager != null && rootSource.CurrencyManager.List is IBindingList) {
                                rootSource.CurrencyManager.Refresh();
                            }
                            _rootEntity = alreadyAddedEntity;
                        } else {

                            rootSource.CurrencyManager.AddUpdatingPosition(entity);
                            //rootSource.CurrencyManager.List.Add(entity);
                            
                            _rootEntity = entity;
                        }

                        if (this.CurrencyManager != null && this.CurrencyManager.List is IBindingList && _rootEntity != null) {
                            var bl = rootSource.CurrencyManager.List as IBindingList;
                            rootSource.CurrencyManager.Position = bl.IndexOf(_rootEntity);
                        }
                        _lastId = UtfprBindingNavigatorBaseHelper.getEntityKeyValue(_rootEntity);
                    }

                    foreach (var detail in UBNDetails) {
                        UtfprBindingNavigatorBaseHelper.boundDataGridsEnabled(detail, true);
                    }
                }

                updateStatusStrip();
            } catch (Exception ex) {
                throw new UtfprWFControlsException("", ex);
            }

            updateNavigationUI();
        }

        /// <summary>
        /// TODO
        /// </summary>
        /// <param name="id"></param>
        public override void QueryById(object id) {
            _lastId = id;
            var bs = (BindingSource)UtfprBindingNavigatorBaseHelper.findRootSourceNavigator(this).DataSource;
            bs.SuspendBinding();

            new UtfprFormProgress(new DoWorkEventHandler(
                delegate {
                    var fetch = UtfprBindingNavigatorBaseHelper.findRootSourceNavigator(this).FetchMode;
                    var rootSource = UtfprBindingNavigatorBaseHelper.findRootSourceNavigator(this);
                    Type entityType = UtfprControlHelper.GetDataSourceType(rootSource.DataSource as BindingSource);

                    var rootForm = UtfprControlHelper.FindForm(this);
                    var info = rootForm.GetType().GetField(UtfprWindowsFormsConsts.UtfprFormCrud_UtfprByKey_FieldName, BindingFlags.Instance | BindingFlags.NonPublic);
                    if (info == null) {
                        throw new ArgumentException("Este formulário precisa de uma propriedade utfprByKey");
                    }

                    var utfprByKey = (UtfprByKey)info.GetValue(rootForm);

                    bool isString = utfprByKey.IsString;
                    bool isCaseInsensitive = utfprByKey.IsCaseInsensitive;

                    try {
                        //EntityObject entity = (EntityObject)EntityDataSource.FindById(entityType, id, isString, isCaseInsensitive, fetch, RelatedEndDescriptions);
                        EntityObject entity = (EntityObject)EntityDataSource.FindById(entityType, _lastId, isString, isCaseInsensitive, fetch, RelatedEndDescriptions);

                        if (entity != null) {
                            this.Invoke(new MethodInvoker(delegate {
                                State.Search();
                            }));

                            var alreadyAddedEntity = rootSource.CurrencyManager.List.Cast<EntityObject>()
                                .SingleOrDefault(e => e.EntityKey == entity.EntityKey);

                            if (alreadyAddedEntity != null) {
                                this.Invoke(new MethodInvoker(delegate {
                                    if (rootSource.CurrencyManager != null && rootSource.CurrencyManager.List is IBindingList) {
                                        rootSource.CurrencyManager.Refresh();
                                    }
                                }));
                                _rootEntity = alreadyAddedEntity;
                            } else {
                                this.Invoke(new MethodInvoker(delegate {
                                    rootSource.CurrencyManager.List.Add(entity);
                                }));
                                _rootEntity = entity;
                            }

                            _lastId = UtfprBindingNavigatorBaseHelper.getEntityKeyValue(_rootEntity);

                            updateStatusStrip();
                        } else {
                            rootSource.CurrentState = UBNStateEnum.Navigating;
                            updateStatusStrip("Id não encontrado.");
                            //throw new EntityNotFoundException("Id não encontrado.");
                        }

                        this.Invoke(new MethodInvoker(delegate {
                            foreach (var detail in UBNDetails) {
                                UtfprBindingNavigatorBaseHelper.boundDataGridsEnabled(detail, true);
                            }
                        }));
                    } catch (Exception ex) {
                        throw new UtfprWFControlsException("", ex);
                    }

                }
            )).ShowDialog();

            bs.ResumeBinding();
            if (this.CurrencyManager != null && this.CurrencyManager.List is IBindingList && _rootEntity != null) {
                var bl = this.CurrencyManager.List as IBindingList;
                this.CurrencyManager.Position = bl.IndexOf(_rootEntity);
            }

            //foreach (var ubn in UBNDetails) {
            //    EntityDataSource.LoadRelatedEnds(ubn.CurrencyManager.Current, ubn.RelatedEndDescriptions);
            //}
            updateNavigationUI();
        }

        #endregion

        #region Properties

        [Category("Data")]
        [Description("The Key Member")]
        [DefaultValue("")]
        [TypeConverter("System.Windows.Forms.Design.DataMemberFieldConverter, System.Design, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")]
        [Editor("UtfprLibrary.Windows.Forms.ComponentModel.UtfprBindingSourceDataMemberFieldEditor, UtfprLibrary.WindowsForms.Design", typeof(UITypeEditor))]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        public override string KeyMember { get; set; }

        private IUtfprRelatedEndDescription[] _relatedEndDescriptions = new UtfprRelatedEndDescription[] { null };
        [Category("Data")]
        [Description("RelatedEnd Descriptions")]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
        [Editor("UtfprLibrary.Windows.Forms.ComponentModel.UtfprEagerLoadEditor, UtfprLibrary.WindowsForms.Design", typeof(UITypeEditor))]
        public override IUtfprRelatedEndDescription[] RelatedEndDescriptions {
            get { return _relatedEndDescriptions; }
            set { _relatedEndDescriptions = value; }
        }

        private bool _allowEditDataGridViews = false;
        [Category("Behavior")]
        [Description("Allow the user to edit data bound DataGridViews")]
        public bool AllowEditDataGridViews {
            get { return _allowEditDataGridViews; }
            set { _allowEditDataGridViews = value; }
        }

        #endregion

        #endregion

        #region Contructor

        /// <summary>
        /// Initializes a new instance of a <see cref="UtfprBindingNavigator"/>.
        /// </summary>
        public UtfprBindingNavigator() {
            InitializeComponent();
        }

        #endregion

        #region Fields

        internal DialogResult _dialogResult = DialogResult.None;
        internal EntityObject _rootEntity;
        internal object _lastId;

        #endregion

        #region EventHandlers

        internal void _queryItem_Click(object sender, EventArgs e) {
            var ce = new CancelEventArgs();
            OnQuerying(ce);
            if (!ce.Cancel) {
                try {
                    Query(ce);
                    OnQueried(e);
                    Debug.WriteLine("Done. Queried.");
                } catch (Exception ex) {
                    OnDataError(new DataErrorEventArgs(ex));
                }
            }
        }

        internal void _saveItem_Click(object sender, EventArgs e) {
            var ce = new CancelEventArgs();
            OnSaving(ce);
            if (!ce.Cancel) {
                try {
                    validateRequiredFields();
                    if (UBNCollectionBaseValidator.IsFormValid(_currentValidatorContainer)) {
                        Save(ce);
                        OnSaved(e);
                        Debug.WriteLine("Done. Saved.");
                    }
                } catch (UpdateException ex) {
                    MessageBox.Show(ex.InnerException.Message, "UpdateException", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    _cancelItem_Click(sender, e);
                } catch (SqlException ex) {
                    MessageBox.Show(ex.Message, "SqlException", MessageBoxButtons.OK, MessageBoxIcon.Error);
                } catch (Exception ex) {
                    updateStatusStrip("Erro ao salvar.");
                    OnDataError(new DataErrorEventArgs(ex));
                }
            }
        }

        internal void _cancelItem_Click(object sender, EventArgs e) {
            var ce = new CancelEventArgs();
            OnCanceling(ce);
            if (!ce.Cancel) {
                try {
                    cancelRequiredFieldsValidation();
                    Cancel(ce);
                    OnCanceled(e);
                    Debug.WriteLine("Done. Canceled.");
                } catch (Exception ex) {
                    OnDataError(new DataErrorEventArgs(ex));
                }
            }
        }

        internal void _deleteItem_Click(object sender, EventArgs e) {
            var ce = new CancelEventArgs();
            OnDeleting(ce);
            if (!ce.Cancel) {
                try {
                    cancelRequiredFieldsValidation();
                    if (CurrentMode == UBNModeEnum.Master) {
                        if (MessageBox.Show("Uma vez que o registro for removido não poderá ser recuperado. Confirma exclusão?", "Remoção de registro.", MessageBoxButtons.YesNo, MessageBoxIcon.Warning) == DialogResult.Yes) {
                            Delete(ce);
                            OnDeleted(e);
                            Debug.WriteLine("Done. Deleted.");
                        }
                    } else if (CurrentMode == UBNModeEnum.Details) {
                        Delete(ce);
                        OnDeleted(e);
                        Debug.WriteLine("Done. Deleted.");
                    }
                } catch (InvalidOperationException ex) {
                    MessageBox.Show(ex.Message, "InvalidOperationException", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    Cancel(ce);
                } catch (Exception ex) {
                    OnDataError(new DataErrorEventArgs(ex));
                }
            }
        }

        internal void _editItem_Click(object sender, EventArgs e) {
            var ce = new CancelEventArgs();
            OnEditing(ce);
            if (!ce.Cancel) {
                try {
                    Edit(ce);
                    OnEdited(e);
                    Debug.WriteLine("Done. Edited.");
                } catch (Exception ex) {
                    OnDataError(new DataErrorEventArgs(ex));
                }
            }
        }

        internal void _addNewItem_Click(object sender, EventArgs e) {
            var ce = new CancelEventArgs();
            OnAddingNew(ce);
            if (!ce.Cancel) {
                try {
                    AddNew(ce);
                    OnAddedNew(e);
                    Debug.WriteLine("Done. AddedNew.");
                } catch (Exception ex) {
                    OnDataError(new DataErrorEventArgs(ex));
                }
            }
        }

        internal void _refreshItem_Click(object sender, EventArgs e) {
            var ce = new CancelEventArgs();
            OnRefreshing(ce);
            if (!ce.Cancel) {
                try {
                    Refresh(ce);
                    OnRefreshed(e);
                    Debug.WriteLine("Done. Refreshed.");
                } catch (Exception ex) {
                    OnDataError(new DataErrorEventArgs(ex));
                }
            }
        }

        internal void _duplicateItem_Click(object sender, EventArgs e) {
            var ce = new CancelEventArgs();
            OnDuplicating(ce);
            if (!ce.Cancel) {
                try {
                    Duplicate(ce);
                    OnDuplicated(e);
                    Debug.WriteLine("Done. Duplicated.");
                } catch (Exception ex) {
                    OnDataError(new DataErrorEventArgs(ex));
                }
            }
        }

        internal void _firstItem_Click(object sender, EventArgs e) {
            try {
                if (this.CurrencyManager != null) {
                    this.CurrencyManager.Position = 0;
                }
                Debug.WriteLine("Done. FirstItem Clicked.");
            } catch (Exception ex) {
                OnDataError(new DataErrorEventArgs(ex));
            }
        }

        internal void _previeousItem_Click(object sender, EventArgs e) {
            try {
                if (this.CurrencyManager != null && this.CurrencyManager.Position > 0) {
                    this.CurrencyManager.Position--;
                }
                Debug.WriteLine("Done. PrevieousItem Clicked.");
            } catch (Exception ex) {
                OnDataError(new DataErrorEventArgs(ex));
            }
        }

        internal void _nextItem_Click(object sender, EventArgs e) {
            try {
                if (this.CurrencyManager != null && this.CurrencyManager.Position < this.CurrencyManager.Count - 1) {
                    this.CurrencyManager.Position++;
                }
                Debug.WriteLine("Done. NextItem Clicked.");
            } catch (Exception ex) {
                OnDataError(new DataErrorEventArgs(ex));
            }
        }

        internal void _lastItem_Click(object sender, EventArgs e) {
            try {
                if (this.CurrencyManager != null) {
                    this.CurrencyManager.Position = this.CurrencyManager.Count - 1;
                }
                Debug.WriteLine("Done. LastItem Clicked.");
            } catch (Exception ex) {
                OnDataError(new DataErrorEventArgs(ex));
            }
        }

        #endregion

        #region Methods

        #region PrivateMethods

        private void InitializeComponent() {
            if (UtfprControlBase.IsInDesignMode) {
                setupCustomIconsAtDesignTime();
            }

            AddNewItem = new UtfprToolStripButton("Novo", this.BaseIcons.AddNewIcon, _addNewItem_Click, "_addNewItem");
            DeleteItem = new UtfprToolStripButton("Remover", this.BaseIcons.DeleteIcon, _deleteItem_Click, " deleteItem");
            EditItem = new UtfprToolStripButton("Editar", this.BaseIcons.EditIcon, _editItem_Click, "editItem");
            CancelItem = new UtfprToolStripButton("Cancelar", this.BaseIcons.CancelIcon, _cancelItem_Click, "cancelItem");
            SaveItem = new UtfprToolStripButton("Salvar", this.BaseIcons.SaveIcon, _saveItem_Click, "saveItem");
            QueryItem = new UtfprToolStripButton("Buscar", this.BaseIcons.QueryIcon, _queryItem_Click, "queryItem");
            RefreshItem = new UtfprToolStripButton("Recarregar", this.BaseIcons.RefreshIcon, _refreshItem_Click, "reloadItem");
            DuplicateItem = new UtfprToolStripButton("Duplicar", this.BaseIcons.DuplicateIcon, _duplicateItem_Click, "duplicateItem");

            foreach (var btn in new ToolStripButton[] { AddNewItem, DeleteItem, EditItem, CancelItem, SaveItem, QueryItem, RefreshItem, DuplicateItem }) {
                btn.DisplayStyle = ToolStripItemDisplayStyle.Image;
                btn.ImageScaling = ToolStripItemImageScaling.None;
                btn.ImageTransparentColor = System.Drawing.Color.Magenta;
                btn.Size = new System.Drawing.Size(52, 52);

                Items.Add(btn);
            }

            SeparatorNavigation = new ToolStripSeparator();
            Items.Add(SeparatorNavigation);

            FirstItem = new UtfprToolStripButton("First Record", this.BaseIcons.FirstRecordIcon, _firstItem_Click, "_firstItem");
            PreviousItem = new UtfprToolStripButton("Previous Record", this.BaseIcons.PreviousRecordIcon, _previeousItem_Click, "_previousItem");
            LabelCurrent = new ToolStripLabel("0 of 0");
            NextItem = new UtfprToolStripButton("Next Record", this.BaseIcons.NextRecordIcon, _nextItem_Click, "_nextItem");
            LastItem = new UtfprToolStripButton("Last Record", this.BaseIcons.LastRecordIcon, _lastItem_Click, "_lastItem");

            foreach (var btn in new ToolStripButton[] { LastItem, NextItem, PreviousItem, FirstItem }) {
                btn.Alignment = ToolStripItemAlignment.Right;
                btn.DisplayStyle = ToolStripItemDisplayStyle.Image;
                Items.Add(btn);
            }

            LabelCurrent.Alignment = ToolStripItemAlignment.Right;
            Items.Insert(11, LabelCurrent);

            if (!UtfprControlBase.IsInDesignMode) {
                if (CurrentMode == UBNModeEnum.Master) {
                    QueryItem.Visible = true;
                } else if (CurrentMode == UBNModeEnum.Details) {
                    QueryItem.Visible = false;
                }
            }

            ////State = Navigating;
            //if (Mode == UBNModeEnum.Details) {
            //    _queryItem.Visible = false;
            //    _refreshItem.Visible = false;
            //    _duplicateItem.Visible = false;
            //}

            createStandartStates();
            setInitialState();

            Dock = DockStyle.None;
            GripStyle = ToolStripGripStyle.Hidden;
            ShowNavigationButtons = false;
            updateNavigationUI();
        }

        private void detachRelatedEntities(EntityObject entity) {
            var entitiesToDetach = new List<object>();

            foreach (var reference in entity.GetRelatedReferences()) {
                if (reference.EntityState == EntityState.Added) {
                    entitiesToDetach.Add(reference);
                }
            }

            foreach (IEnumerable collection in entity.GetRelatedCollections()) {
                foreach (EntityObject e in collection) {
                    if (e.EntityState == EntityState.Added || e.EntityState == EntityState.Modified) {
                        entitiesToDetach.Add(e);
                    }
                }
            }

            if (entity.EntityState == EntityState.Added) {
                entitiesToDetach.Add(entity);
            }

            foreach (var e in entitiesToDetach) {
                EntityDataSource.ObjectContext.Detach(e);
            }
        }

        private void refreshModifiedEntities(RefreshMode refreshMode) {
            // delete modified objects that did not saved
            foreach (var entry in EntityDataSource.ObjectContext.ObjectStateManager.GetObjectStateEntries(EntityState.Modified)) {
                // exclude relationships from stateEntries object
                if (entry.Entity != null) {
                    EntityDataSource.ObjectContext.Refresh(refreshMode, entry.Entity);
                }
            }
        }

        private void queryByLastId() {
            var fetch = UtfprBindingNavigatorBaseHelper.findRootSourceNavigator(this).FetchMode;
            var rootSource = UtfprBindingNavigatorBaseHelper.findRootSourceNavigator(this);
            Type entityType = UtfprControlHelper.GetDataSourceType(rootSource.DataSource as BindingSource);

            var rootForm = UtfprControlHelper.FindForm(this);
            var info = rootForm.GetType().GetField(UtfprWindowsFormsConsts.UtfprFormCrud_UtfprByKey_FieldName, BindingFlags.Instance | BindingFlags.NonPublic);
            if (info == null) {
                throw new ArgumentException("Este formulário precisa de uma propriedade utfprByKey");
            }

            var utfprByKey = (UtfprByKey)info.GetValue(rootForm);

            bool isString = utfprByKey.IsString;
            bool isCaseInsensitive = utfprByKey.IsCaseInsensitive;

            var entity = (EntityObject)EntityDataSource.FindById(entityType, _lastId, isString, isCaseInsensitive, fetch, RelatedEndDescriptions);

            if (entity != null) {
                this.Invoke(new MethodInvoker(delegate {
                    State.Search();
                }));

                var alreadyAddedEntity = rootSource.CurrencyManager.List.Cast<EntityObject>()
                    .SingleOrDefault(e => e.EntityKey == entity.EntityKey);

                if (alreadyAddedEntity != null) {
                    this.Invoke(new MethodInvoker(delegate {
                        if (rootSource.CurrencyManager != null && rootSource.CurrencyManager.List is IBindingList) {
                            rootSource.CurrencyManager.Refresh();
                        }
                    }));
                    _rootEntity = alreadyAddedEntity;
                } else {
                    this.Invoke(new MethodInvoker(delegate {
                        //here I'm kind of refreshing the lazily loaded entities
                        if (FetchMode == FetchModeEnum.Lazy) {
                            if (RelatedEndDescriptions != null) {
                                foreach (var description in RelatedEndDescriptions) {
                                    ((IRelatedEnd)entity.GetType()
                                        .GetProperty(description.RelatedEndName)
                                        .GetValue(entity, null)).Load(MergeOption.OverwriteChanges);
                                }
                            }
                        }

                        rootSource.CurrencyManager.List.Add(entity);
                    }));
                    _rootEntity = entity;
                }

                _lastId = UtfprBindingNavigatorBaseHelper.getEntityKeyValue(_rootEntity);

                updateStatusStrip();
            } else {
                rootSource.CurrentState = UBNStateEnum.Navigating;
                updateStatusStrip("Id não encontrado.");
            }

            this.Invoke(new MethodInvoker(delegate {
                if (this.CurrencyManager != null && this.CurrencyManager.List is IBindingList && _rootEntity != null) {
                    var bl = this.CurrencyManager.List as IBindingList;
                    this.CurrencyManager.Position = bl.IndexOf(_rootEntity);
                }

                foreach (var detail in UBNDetails) {
                    UtfprBindingNavigatorBaseHelper.boundDataGridsEnabled(detail, true);
                }
            }));
        }

        #endregion

        #region InternalMethods

        #region Validation

        internal IContainer _validatorContainer {
            get {
                IContainer container = new Container();
                foreach (var validator in getRequiredFieldsValidators()) {
                    container.Add(validator);
                }
                return container;
            }
        }

        internal IContainer _currentValidatorContainer {
            get {
                IContainer container = new Container();
                foreach (var validator in getRequiredFieldsValidators()) {
                    if (validator.UtfprBindingNavigator == this) {
                        container.Add(validator);
                    }
                }
                return container;
            }
        }

        internal List<UBNRequiredFieldsValidator> getRequiredFieldsValidators() {
            var requiredFieldsValidators = new List<UBNRequiredFieldsValidator>();
            var flags = BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly;
            var fields = FindForm().GetType().GetFields(flags)
                .Where(f => f.FieldType == typeof(UBNRequiredFieldsValidator));

            foreach (var field in fields) {
                requiredFieldsValidators.Add(field.GetValue(FindForm()) as UBNRequiredFieldsValidator);
            }

            return requiredFieldsValidators;
        }

        internal void validateRequiredFields() {
            foreach (var validator in getRequiredFieldsValidators()) {
                if (validator.UtfprBindingNavigator == this) {
                    validator.Validate();
                }
            }
        }

        internal void cancelRequiredFieldsValidation() {
            foreach (var validator in getRequiredFieldsValidators()) {
                validator.Validate();
                validator.CancelValidation();
            }
        }

        #endregion

        #endregion

        #endregion
    }
}

